//Drifting Thoughts Car script with flight
//by Kayla Stonecutter
//
//


//Basic settings
list        ForwardPowerGears = [30, 40,50, 60]; //Forward power for each gear, any number of gears. Jump/Crouch to change gears
float       ReversePower = -10; //Reverse power
float       TurnPower = 7; //Turning power
float       TurnSpeedAdjust = 1.0; //how much effect speed has on turning, higher numbers effect more. 0.0 to disable

float       FlightForwardPower = 24; //Power for flight modes
float       FlightReversePower = -16;
float       FlightUpPower = 14;
float       FlightDownPower = -14;
float       FlightStrafePower = 12;
float       FlightTurnPower = 4;


string      SitText = "Drive"; //Text to show on pie menu
string      NonOwnerMessage = "You are not the owner of this vehicle ..."; //Message when someone other than owner tries to sit

integer     ListenCh = 0; //channel to listen for commands
string      HornCommand = "h"; //command to sound the horn
string      RevCommand = "r"; //command to rev the engine
string      IdleCommand = "i"; //command to start engine
string      StopCommand = "s"; //command to stop engine
string      FlightCommand = "f"; //command to switch to flight mode
string      GroundCommand = "g"; //command to switch to ground mode

integer     EnableFlight = 0;//set to 0 to not allow flight mode

//You can leave Horn, Rev, and Startup sounds at "" to not use them. Idle, Run, and Flight must be filled (Idle and Run can be the same)
//sounds must either be the name of a sound in the contents, or a UUID. Horn and Rev sounds go with the Horn/Rev script in a different prim
string      FlightSound = "Flight"; //Sound to play while flying
string      HornSound = "Horn"; //Sound to play for horn
string      RevSound = ""; //Sound to play for engine rev
string      IdleSound = "Idle"; //Sound to play when idling
string      RunSound = "Run"; //Sound to play when the gas in pressed
string      StartupSound = "Startup"; //Sound to play when owner sits
string      CollisionSound = ""; //Sound to play when colliding with something else

vector      SitTarget = <0.1,0.0,01>; //Position relative to root to place driver
vector      SitRotation = <0, 0, 0>; //Rotation of the avatar, in degrees
string      DrivingAnim = "Driving"; //Animation to play when owner sits, leave blank for no anim

//Other variables
key         Owner;
string      OwnerName;
float       ForwardPower;
integer     NumGears;
integer     Gear;
integer     NewSound;
string      Sound;
integer     CurDir;
integer     LastDir;
integer     Forward;
vector      SpeedVec;
vector      Linear;
vector      Angular;
integer     Active;
key         sitting;
integer     Moving;
string      SimName;

integer     DIR_STOP = 100;
integer     DIR_START = 101;
integer     DIR_NORM = 102;
integer     DIR_LEFT = 103;
integer     DIR_RIGHT = 104;
integer     DIR_FLIGHT = 105;


default
{
    state_entry()
    {
        if(llGetInventoryType("DT Unpacker") == -1)
        {
            Owner = llGetOwner();
            OwnerName = llKey2Name(Owner);
            TurnSpeedAdjust *= 0.01;
            ForwardPower = llList2Integer(ForwardPowerGears, 0);
            NumGears = llGetListLength(ForwardPowerGears);
            llSetSitText(SitText);
            llCollisionSound(CollisionSound, 1.0);
            llSitTarget(SitTarget, llEuler2Rot(SitRotation * DEG_TO_RAD));
            state Ground;
        }
    }
    
    on_rez(integer param)
    {
        llResetScript();
    }
}

state Ground
{
    state_entry()
    {
        llListen(ListenCh, OwnerName, NULL_KEY, "");
        llStopSound();
        if(!Active)
        {
            llSetStatus(STATUS_PHYSICS, FALSE);
            llMessageLinked(LINK_ALL_CHILDREN , DIR_STOP, "", NULL_KEY);
            llUnSit(llAvatarOnSitTarget());
        }else{
            SimName = llGetRegionName();
            llMessageLinked(LINK_ALL_CHILDREN, DIR_START, "", NULL_KEY);
            llMessageLinked(LINK_ALL_CHILDREN, DIR_NORM, "", NULL_KEY);
            NewSound = 1;
            Sound = IdleSound;
            Linear = <0,0,-2>;
            Gear = 0;
            ForwardPower = llList2Integer(ForwardPowerGears, 0);
            llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN | CONTROL_LBUTTON, TRUE, FALSE);
        }
        
        llSetVehicleType(VEHICLE_TYPE_CAR);
        llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.80);
        llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 1.0);
        
        llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.2);
        llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 0.1);
        
        llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 1.0);
        llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 0.1);
        
        llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.3);
        llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.1);
        
        llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <100.0, 2.0, 1000.0> );
        llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, <10000.0, 10000.0, 10000.0> );
        
        llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 10000.0);
        llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 0.0);
        
        llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 10000 );
        llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY, 0 );
        llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 100 );
        llSetVehicleFloatParam(VEHICLE_BUOYANCY, 0 );
        
        llRemoveVehicleFlags(VEHICLE_FLAG_HOVER_WATER_ONLY | VEHICLE_FLAG_HOVER_TERRAIN_ONLY | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);
        
        llSetVehicleFlags(VEHICLE_FLAG_NO_DEFLECTION_UP | VEHICLE_FLAG_LIMIT_ROLL_ONLY | VEHICLE_FLAG_HOVER_UP_ONLY | VEHICLE_FLAG_LIMIT_MOTOR_UP);
    }
    
    on_rez(integer param)
    {
        llResetScript();
    }
    
    listen(integer channel, string name, key id, string message)
    {
        if(llGetOwnerKey(id) != Owner) return;
        message = llToLower(message);
        if((message == HornCommand) && (HornSound != "Horn")) llMessageLinked(LINK_SET, 12345, HornSound, NULL_KEY);
        else if((message == RevCommand) && (RevSound != "")) llMessageLinked(LINK_SET, 12345, RevSound, NULL_KEY);
        else if(message == IdleCommand) llLoopSound(IdleSound, 1.0);
        else if(message == StopCommand) llStopSound();
        else if((message == FlightCommand) && Active && EnableFlight) state Flight;
    }
    
    changed(integer change)
    {
        if((change & CHANGED_LINK) == CHANGED_LINK)
        {
            sitting = llAvatarOnSitTarget();
            if(sitting)
            {
                if(sitting != llGetOwner())
                {
                    llWhisper(0, NonOwnerMessage);
                    llUnSit(sitting);
                }else if(!Active)
                {
                    llRequestPermissions(Owner, PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
                    if(StartupSound != "") llTriggerSound(StartupSound, 1.0);
                    llMessageLinked(LINK_ALL_CHILDREN, DIR_START, "", NULL_KEY);
                    llSetPos(llGetPos() + <0,0,0.15>);
                    llSetStatus(STATUS_PHYSICS, TRUE);
                    SimName = llGetRegionName();
                    llLoopSound(IdleSound, 1.0);
                    llSetTimerEvent(0.1);
                    CurDir = DIR_NORM;
                    LastDir = DIR_NORM;
                }
            }else if(Active)
            {
                llSetTimerEvent(0.0);
                if(DrivingAnim != "") llStopAnimation(DrivingAnim);
                Active = 0;
                llStopSound();
                llSetStatus(STATUS_PHYSICS, FALSE);
                llMessageLinked(LINK_ALL_CHILDREN , DIR_STOP, "", NULL_KEY);
                llReleaseControls();
            }
        }
    }
    
    run_time_permissions(integer perms)
    {
        if(perms == (PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA))
        {
            Active = 1;
            Gear = 0;
            Linear = <0,0,-2>;
            Angular = <0,0,0>;
            if(DrivingAnim != "")
            {
                llStopAnimation("sit");
                llStartAnimation(DrivingAnim);
            }
            llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_DOWN | CONTROL_UP | CONTROL_RIGHT | CONTROL_LEFT | CONTROL_ROT_RIGHT | CONTROL_ROT_LEFT, TRUE, FALSE);
            llSetCameraParams([
                CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
                CAMERA_BEHINDNESS_ANGLE, 2.0, // (0 to 180) degrees
                CAMERA_BEHINDNESS_LAG, 0.1, // (0 to 3) seconds
                CAMERA_DISTANCE, 8.0, // ( 0.5 to 10) meters
                // CAMERA_FOCUS, <0,0,0>, // region-relative position
                CAMERA_FOCUS_LAG, 0.1 , // (0 to 3) seconds
                CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
                CAMERA_FOCUS_THRESHOLD, 0.5, // (0 to 4) meters
                CAMERA_PITCH, 20.0, // (-45 to 80) degrees
                // CAMERA_POSITION, <0,0,0>, // region-relative position
                CAMERA_POSITION_LAG, 0.1, // (0 to 3) seconds
                CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
                CAMERA_POSITION_THRESHOLD, 0.5, // (0 to 4) meters
                CAMERA_FOCUS_OFFSET, <0,0,0> // <-10,-10,-10> to <10,10,10> meters
            ]);
        }
    }
    
    control(key id, integer levels, integer edges)
    {
        if((edges & levels & CONTROL_FWD) && (edges & levels & CONTROL_BACK) && EnableFlight) state Flight;
        
        if(!Moving)
        {
            Moving = 1;
            llSetStatus(STATUS_PHYSICS, TRUE);
        }
        
        SpeedVec = llGetVel() / llGetRot();
        
        if((edges & levels & CONTROL_UP))
        {
            if((Gear + 1) != NumGears)
            {
                ++Gear;
                llWhisper(0, "Gear " + (string)(Gear + 1));
                ForwardPower = llList2Float(ForwardPowerGears, Gear);
                if(Linear.x > 0) Linear.x = ForwardPower;
            }
        }else if((edges & levels & CONTROL_DOWN))
        {
            if((Gear - 1) != -1)
            {
                --Gear;
                llWhisper(0, "Gear " + (string)(Gear + 1));
                ForwardPower = llList2Float(ForwardPowerGears, Gear);
                if(Linear.x > 0) Linear.x = ForwardPower;
            }
        }
        
        if((edges & levels & CONTROL_FWD))
        {
            Linear.x = ForwardPower;
            NewSound = 1;
            Sound = RunSound;
        }else if((edges & levels & CONTROL_BACK))
        {
            Linear.x = ReversePower;
            NewSound = 1;
            Sound = RunSound;
        }else if((edges & ~levels & CONTROL_FWD) || (edges & ~levels & CONTROL_BACK))
        {
            Linear.x = 0.0;
            NewSound = 1;
            Sound = IdleSound;
        }
        
        if(llFabs(SpeedVec.x) < 0.5)
        {
            if(levels & CONTROL_ROT_LEFT) CurDir = DIR_LEFT;
            else if(levels & CONTROL_ROT_RIGHT) CurDir = DIR_RIGHT;
            else CurDir = DIR_NORM;
            Angular.z = 0.0;
        }else{
            if(SpeedVec.x < 0.0)
            {
                Forward = -1;
                SpeedVec.x *= -TurnSpeedAdjust;
            }else{
                Forward = 1;
                SpeedVec.x *= TurnSpeedAdjust;
            }
            
            if(levels & CONTROL_ROT_LEFT)
            {
                CurDir = DIR_LEFT;
                Angular.z = (TurnPower - SpeedVec.x) * Forward;
            }else if(levels & CONTROL_ROT_RIGHT)
            {
                CurDir = DIR_RIGHT;
                Angular.z = -((TurnPower - SpeedVec.x) * Forward);
            }else if((edges & ~levels & CONTROL_ROT_LEFT) || (edges & ~levels & CONTROL_ROT_RIGHT))
            {
                CurDir = DIR_NORM;
                Angular.z = 0;
            }
        }
    }
    
    moving_end()
    {
        if(llGetRegionName() == SimName)
        {
            Moving = 0;
            llSetStatus(STATUS_PHYSICS, FALSE);
        }else{
            SimName = llGetRegionName();
        }
    }
    
    timer()
    {
        if(Linear != <0.0,  0.0, -2.0>)
        {
            llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, Linear);
            llApplyImpulse(Linear, TRUE);
        }
        if(Angular != <0.0, 0.0, 0.0>) llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, Angular);
        if(CurDir != LastDir)
        {
            llMessageLinked(LINK_ALL_CHILDREN, CurDir, "", NULL_KEY);
            LastDir = CurDir;
        }
        if(NewSound)
        {
            llStopSound();
            NewSound = 0;
            llLoopSound(Sound, 1.0);
        }
    }
}

state Flight
{
    state_entry()
    {
        Linear = <0,0,0>;
        llStopSound();
        llMessageLinked(LINK_ALL_CHILDREN, DIR_FLIGHT, "", NULL_KEY);
        llLoopSound(FlightSound, 1.0);
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetPos(llGetPos() + <0,0,0.25>);
        vector rot = llRot2Euler(llGetRot());
        llSetRot(llEuler2Rot(<0,0,rot.z>));
        llListen(ListenCh, OwnerName, NULL_KEY, "");
        llSetVehicleType(VEHICLE_TYPE_AIRPLANE);
        
        // linear friction
        llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <100.0, 100.0, 100.0>);
        
        // uniform angular friction
        llSetVehicleFloatParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, 0.5);
        
        // linear motor
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0.0, 0.0, 0.0>);
        llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 1.0);
        llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 1.0);
        
        // angular motor
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0.0, 0.0, 0.0>);
        llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 1.0);
        llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 2.0);
        
        // hover
        llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0.0);
        llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY, 0.0);
        llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 360.0);
        llSetVehicleFloatParam(VEHICLE_BUOYANCY, 0.988);
        
        // linear deflection
        llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.0);
        llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 1.0);
        
        // angular deflection
        llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.25);
        llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 100.0);
        
        // vertical attractor
        llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5);
        llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 1.0);
        
        // banking
        llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, 0.0);
        llSetVehicleFloatParam(VEHICLE_BANKING_MIX, 1.0);
        llSetVehicleFloatParam(VEHICLE_BANKING_TIMESCALE, 1.0);
        
        // default rotation of local frame
        llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, <0.00000, 0.00000, 0.00000, 0.00000>);
        
        // removed vehicle flags
        llRemoveVehicleFlags(VEHICLE_FLAG_NO_DEFLECTION_UP | VEHICLE_FLAG_HOVER_WATER_ONLY | VEHICLE_FLAG_HOVER_TERRAIN_ONLY | VEHICLE_FLAG_HOVER_UP_ONLY | VEHICLE_FLAG_LIMIT_MOTOR_UP | VEHICLE_FLAG_LIMIT_ROLL_ONLY);
        
        // set vehicle flags
        llSetVehicleFlags(VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);
        llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN | CONTROL_LBUTTON, TRUE, FALSE);
        
        llSetStatus(STATUS_PHYSICS, TRUE);
    }
    
    on_rez(integer param)
    {
        llResetScript();
    }
    
    listen(integer channel, string name, key id, string message)
    {
        if(llGetOwnerKey(id) != Owner) return;
        message = llToLower(message);
        if((message == HornCommand) && (HornSound != "Horn")) llMessageLinked(LINK_SET, 12345, HornSound, NULL_KEY);
        else if(message == GroundCommand) state Ground;
    }
    
    control(key name, integer levels, integer edges)
    {
        if((edges & levels & CONTROL_FWD) && (edges & levels & CONTROL_BACK)) state Ground;
        
        if((levels & CONTROL_LBUTTON))
        {
            llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0,0,0>);
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,0>);
            llSetStatus(STATUS_PHYSICS, FALSE);
            llSleep(0.1);
            llSetStatus(STATUS_PHYSICS, TRUE);
            return;
        }
        
        if((edges & levels & CONTROL_UP)) Linear.z = FlightUpPower;
        else if((edges & levels & CONTROL_DOWN)) Linear.z = FlightDownPower;
        else if((edges & ~levels & CONTROL_UP) || (edges & ~levels & CONTROL_DOWN)) Linear.z = 0.0;

        if((edges & levels & CONTROL_FWD)) Linear.x = FlightForwardPower;
        else if((edges & levels & CONTROL_BACK)) Linear.x = FlightReversePower;
        else if((edges & ~levels & CONTROL_FWD) || (edges & ~levels & CONTROL_BACK)) Linear.x = 0.0;
        
        if((edges & levels & CONTROL_LEFT)) Linear.y = FlightStrafePower;
        else if((edges & levels & CONTROL_RIGHT)) Linear.y = -FlightStrafePower;
        else if((edges & ~levels & CONTROL_LEFT) || (edges & ~levels & CONTROL_RIGHT)) Linear.y = 0.0;
        
        if((edges & levels & CONTROL_ROT_LEFT)) Angular.z = FlightTurnPower;
        else if((edges & levels & CONTROL_ROT_RIGHT)) Angular.z = -FlightTurnPower;
        else if((edges & ~levels & CONTROL_ROT_LEFT) || (edges & ~levels & CONTROL_ROT_RIGHT)) Angular.z = 0.0;
    }
    
    changed(integer change)
    {
        if((change & CHANGED_LINK) == CHANGED_LINK)
        {
            sitting = llAvatarOnSitTarget();
            if(sitting == NULL_KEY)
            {
                llSetTimerEvent(0.0);
                if(DrivingAnim != "") llStopAnimation(DrivingAnim);
                Active = 0;
                llStopSound();
                llSetStatus(STATUS_PHYSICS, FALSE);
                llMessageLinked(LINK_ALL_CHILDREN , DIR_STOP, "", NULL_KEY);
                llReleaseControls();
                state Ground;
            }
        }
    }
    
    timer()
    {
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, Linear);

        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, Angular);
    }
}